/*
 * Copyright (C) 2011-present ShenZhen iBOXCHAIN Information Technology Co.,Ltd.
 *
 * All right reserved.
 *
 * This software is the confidential and proprietary
 * information of iBOXCHAIN Company of China.
 * ("Confidential Information"). You shall not disclose
 * such Confidential Information and shall use it only
 * in accordance with the terms of the contract agreement
 * you entered into with iBOXCHAIN inc.
 */
package com.example.wac.shiro.config;

import com.example.wac.shiro.ReloadUrlPermsByDBHelper;
import com.example.wac.shiro.RestShiroFilterFactoryBean;
import com.example.wac.shiro.filter.RestAuthorizationFilter;
import com.example.wac.shiro.filter.RestFormAuthenticationFilter;
import com.example.wac.shiro.listener.CustomSessionListener;
import com.example.wac.shiro.realm.MyShiroRealm;
import com.example.wac.shiro.redis.RedisMsgProducer;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author wuancheng
 * @description
 * @date 2023-04-13 15:20:00
 */

@Configuration
@ConditionalOnProperty(prefix = "shiro", name = "auto-config", havingValue = "true", matchIfMissing = true)
public class EnableShiroAutoConfig {
    @Autowired
    private ShiroConfig config;

    @Autowired
    private JedisPool jedisPool;

    @Autowired
    private ReloadUrlPermsByDBHelper reloadUrlPermsByDbHelper;

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        RestShiroFilterFactoryBean shiroFilterFactoryBean = new RestShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        // 只有perms，roles，ssl，rest，port才是属于AuthorizationFilter，而anon，authcBasic，authc，user是AuthenticationFilter，所以unauthorizedUrl设置后不起作用，只会在控制台打印异常信息。
        // 参考:http://www.jianshu.com/p/e03f5b54838c
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        filters.put("authc", new RestFormAuthenticationFilter());
        filters.put("perms", new RestAuthorizationFilter());
        Map<String, String> urlPermsMap = reloadUrlPermsByDbHelper.getUrlPermsMap();
        shiroFilterFactoryBean.setFilterChainDefinitionMap(urlPermsMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法:这里使用SHA-256算法;
        hashedCredentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
        // 散列的次数，比如散列两次，相当于 sha-256(sha-256(""));
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }

    /**
     * 注入 securityManager
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        securityManager.setSessionManager(sessionManager());
        securityManager.setRememberMeManager(cookieRememberMeManager());
        securityManager.setCacheManager(redisCacheManager());
        return securityManager;
    }

    @Bean
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setExpire((config.getGlobalSessionTimeout() / 1000 * 2));
        redisCacheManager.setPrincipalIdFieldName(config.getPrincipalIdFieldName());
        return redisCacheManager;
    }


    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setJedisPool(jedisPool);
        return redisManager;
    }

    @Bean
    public RedisMsgProducer redisMsgProducer() {
        RedisMsgProducer redisMsgProducer = new RedisMsgProducer();
        redisMsgProducer.setJedisPool(jedisPool);
        return redisMsgProducer;
    }

    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        // 单位秒
        redisSessionDAO.setExpire((config.getGlobalSessionTimeout() / 1000 * 2));
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setSessionInMemoryEnabled(false);
        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
        return redisSessionDAO;
    }

    @Bean
    public CookieRememberMeManager cookieRememberMeManager() {
        CookieRememberMeManager manager = new CookieRememberMeManager();
        manager.setCookie(rememberMeCookie());
        return manager;
    }

    @Bean
    public JavaUuidSessionIdGenerator sessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie simpleCookie = new SimpleCookie(config.getRememberCookieName());
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(604800);
        return simpleCookie;
    }

    @Bean
    public SimpleCookie sessionIdCookie() {
        // cookie名称默认为JSESSIONID
        SimpleCookie simpleCookie = new SimpleCookie(config.getCookieName());
        simpleCookie.setPath("/");
        simpleCookie.setHttpOnly(true);
        // 记住我cookie生效时间30天 ,单位秒
        // 注意：这里如果设置了时间，关闭浏览器后重新打开浏览器会话还在
        // simpleCookie.setMaxAge(2592000);
        return simpleCookie;
    }

    @Bean
    public CustomSessionListener customSessionListener() {
        CustomSessionListener customSessionListener = new CustomSessionListener();
        customSessionListener.setRedisManager(redisManager());
        return customSessionListener;
    }

    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationInterval(3600000);
        sessionManager.setGlobalSessionTimeout(config.getGlobalSessionTimeout());
        sessionManager.setSessionDAO(redisSessionDAO());
        List<SessionListener> list = new ArrayList<SessionListener>(1);
        list.add(customSessionListener());
        sessionManager.setSessionListeners(list);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        return sessionManager;
    }

}
